{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 利用传统方法检测车道线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "src = np.float32([[200, 720], [1100, 720], [595, 450], [685, 450]])\n",
    "dst = np.float32([[300, 720], [980, 720], [300, 0], [980, 0]])\n",
    "m_inv = cv2.getPerspectiveTransform(dst, src)\n",
    "m = cv2.getPerspectiveTransform(src, dst)\n",
    "\n",
    "\n",
    "def abs_sobel_threshold(img, orient='x', thresh_min=2, thresh_max=100000):\n",
    "    \n",
    "    ###利用X，y方向上sobel，二值化图像######\n",
    "    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n",
    "    if orient == 'x':\n",
    "        abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 1, 0))\n",
    "    if orient == 'y':\n",
    "        ########参考求x方向的sobel算子，计算y方向上sobel算子#######\n",
    "        #############填空1 （1行代码）########################\n",
    "        abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 0, 1))\n",
    "        #############填空1 （1行代码）########################\n",
    "        \n",
    "\n",
    "    scaled_sobel = np.uint8(255 * abs_sobel / np.max(abs_sobel))\n",
    "    binary_output = np.zeros_like(scaled_sobel)\n",
    "    \n",
    "    #############二值图像，大于最小阈值并且小于最大阈值的区间置为255， 其余为0，可通过修改最大最小值查看差异######\n",
    "    ##############填空2（1行代码）########################\n",
    "    binary_output[(scaled_sobel >= thresh_min) & (scaled_sobel <= thresh_max)] = 255    \n",
    "    #############填空2 （1行代码）########################\n",
    "   \n",
    "    return binary_output\n",
    "\n",
    "\n",
    "def mag_threshold(img, sobel_kernel=3, mag_threshold=(20, 1000)):\n",
    "    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n",
    "    sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=sobel_kernel)\n",
    "    sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=sobel_kernel)\n",
    "    \n",
    "    ########根据x方向的sobel算子和y方向上sobel算子，计算梯度，公式为sqrt（x^2 + y ^2）#######\n",
    "    #############填空3 （1行代码）########################\n",
    "    gradmag = np.sqrt(sobelx ** 2 + sobely ** 2)\n",
    "\n",
    "    #############填空3 （1行代码）########################\n",
    "    scale_factor = np.max(gradmag) / 255\n",
    "    gradmag = (gradmag / scale_factor).astype(np.uint8)\n",
    "    binary_out = np.zeros_like(gradmag)\n",
    "    ########转换为二值图，最大最小值可调，kernel_size也可以调整看看差异#######\n",
    "    #############填空4 （1行代码）########################\n",
    "    binary_out[(gradmag >= 30) & (gradmag <= 120)] = 255\n",
    "\n",
    "    #############填空4 （1行代码）########################\n",
    "    \n",
    "\n",
    "    return binary_out\n",
    "\n",
    "def dir_threshold(img, sobel_kernel=5, thresh=(0, np.pi/4)):\n",
    "\tgray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n",
    "\tsobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=sobel_kernel)\n",
    "\tsobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=sobel_kernel)\n",
    "    \n",
    "    ########根据x方向的sobel算子和y方向上sobel算子，计算角度，公式为arctan（y/x），将倾斜角度过大的过滤掉#######\n",
    "    #############填空5 （1行代码）########################    \n",
    "\tabsgraddir = np.arctan2(np.absolute(sobely), np.absolute(sobelx))\n",
    "    #############填空5 （1行代码）########################\n",
    "    \n",
    "\tbinary_output = np.zeros_like(absgraddir)\n",
    "    \n",
    "    ########转换为二值图，最大最小值可调，kernel_size也可以调整看看差异#######\n",
    "    #############填空6 （1行代码）########################\n",
    "\tbinary_output[(absgraddir >= thresh[0]) & (absgraddir <= thresh[1])] = 255\n",
    "    \n",
    "    #############填空6 （1行代码）########################\n",
    "\treturn binary_output\n",
    "\n",
    "\n",
    "def hls_thresh(img, thresh=(100, 255)):\n",
    "\n",
    "\thls = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)\n",
    "    ########分离出s通道s_channel#######\n",
    "    #############填空7 （1行代码）########################\n",
    "\ts_channel = hls[:, :, 2]\n",
    "    \n",
    "    #############填空7 （1行代码）########################\n",
    "\tbinary_output = np.zeros_like(s_channel)\n",
    "    \n",
    "    ########转换为二值图，最大最小值可调#######\n",
    "    #############填空8 （1行代码）########################\n",
    "\tbinary_output[(s_channel > thresh[0]) & (s_channel <= thresh[1])] = 255\n",
    "    \n",
    "    #############填空8 （1行代码）########################\n",
    "\treturn binary_output\n",
    "\n",
    "def combined_threshold(img):\n",
    "\tabs_bin = abs_sobel_threshold(img, orient='x', thresh_min=50, thresh_max=255)\n",
    "\tmag_bin = mag_threshold(img, sobel_kernel=3, mag_threshold=(50, 255))\n",
    "\tdir_bin = dir_threshold(img, sobel_kernel=15, thresh=(0.7, 1.3))\n",
    "\thls_bin = hls_thresh(img, thresh=(170, 255))\n",
    "\n",
    "\tcombined = np.zeros_like(dir_bin)\n",
    "    #############组合四个阈值结果，判定车道线，##########\n",
    "    #########例如(abs_bin == 255 | ((mag_bin == 255) & (dir_bin == 255))) | hls_bin == 25）#\n",
    "    ##########可以尝试不同的组合######################\n",
    "    #############填空9（1行代码）########################\n",
    "\tcombined[(abs_bin == 255 | ((mag_bin == 255) & (dir_bin == 255))) | hls_bin == 255] = 255\n",
    "    \n",
    "    #############填空9 （1行代码）########################\n",
    "\n",
    "\treturn combined, abs_bin, mag_bin, dir_bin, hls_bin\n",
    "\n",
    "def line_fit_and_draw_line(binary_warped):\n",
    "    # \"查找拟合直线\"\n",
    "    # 对图像对下半部分查找直方图\n",
    "    #############填空10（1行代码）截取图像高度的下方1/2处########################\n",
    "    \n",
    "    histogram = np.sum(binary_warped[binary_warped.shape[0] // 2:, :], axis=0)\n",
    "    \n",
    "    #############填空10（1行代码）截取图像高度的下方1/2处########################\n",
    "    out_img = (np.dstack((binary_warped, binary_warped, binary_warped)) * 255).astype('uint8')\n",
    "\n",
    "    #查找直方图中左右两侧对峰值\n",
    "    midpoint = np.int(histogram.shape[0] / 2)\n",
    "    \n",
    "    #左侧从100到 midpoint的最大值，转换成图像坐标还要加上100哦～############\n",
    "    #右侧从midpoint到图像宽度减100的最大值，转换成图像坐标还要加上midpoint哦～############\n",
    "    ####也就是图像左右边缘100像素内不查找车道线##################\n",
    "    \n",
    "    #############填空11（2行代码）查找左侧右侧最大值基本点########################\n",
    "    leftx_base = np.argmax(histogram[100: midpoint]) + 100\n",
    "    rightx_base = np.argmax(histogram[midpoint: -100]) + midpoint\n",
    "    #############填空11（2行代码）查找左侧右侧最大值基本点########################\n",
    "\n",
    "    ##########以下是关于滑动窗口查找车道线的代码#####################\n",
    "    nwindows = 9\n",
    "    window_height = np.int(binary_warped.shape[0] / nwindows)\n",
    "    nonzero = binary_warped.nonzero()\n",
    "    nonzeroy = np.array(nonzero[0])\n",
    "    nonzerox = np.array(nonzero[1])\n",
    "\n",
    "    leftx_current = leftx_base\n",
    "    rightx_current = rightx_base\n",
    "\n",
    "    margin = 100\n",
    "    minpix = 10\n",
    "\n",
    "    left_lane_inds = []\n",
    "    right_lane_inds = []\n",
    "\n",
    "    for window in range(nwindows):\n",
    "        win_y_low = binary_warped.shape[0] - (window + 1) * window_height\n",
    "        win_y_high = binary_warped.shape[0] - window * window_height\n",
    "        win_xleft_low = leftx_current - margin\n",
    "        win_xleft_high = leftx_current + margin\n",
    "        win_xright_low = rightx_current - margin\n",
    "        win_xright_high = rightx_current + margin\n",
    "\n",
    "        good_left_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & (nonzerox > win_xleft_low) & (nonzerox < win_xleft_high)).nonzero()[0]\n",
    "        good_right_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & (nonzerox >= win_xright_low) & (nonzerox < win_xright_high)).nonzero()[0]\n",
    "\n",
    "        left_lane_inds.append(good_left_inds)\n",
    "        right_lane_inds.append(good_right_inds)\n",
    "\n",
    "        if len(good_left_inds) > minpix:\n",
    "            leftx_current = np.int(np.mean(nonzerox[good_left_inds]))\n",
    "        if len(good_right_inds) > minpix:\n",
    "            rightx_current = np.int(np.mean(nonzerox[good_right_inds]))\n",
    "\n",
    "    left_lane_inds = np.concatenate(left_lane_inds)\n",
    "    right_lane_inds = np.concatenate(right_lane_inds)\n",
    "\n",
    "    leftx = nonzerox[left_lane_inds]\n",
    "    lefty = nonzeroy[left_lane_inds]\n",
    "    rightx = nonzerox[right_lane_inds]\n",
    "    righty = nonzeroy[right_lane_inds]\n",
    "    ##########以上是关于滑动窗口查找车道线的代码#####################\n",
    "    \n",
    "    \n",
    "    #将左侧，右侧车道线3次拟合，用函数np.polyfit##########\n",
    "    #############填空12（2行代码）左侧、右侧车道线拟合#######################\n",
    "    left_fit = np.polyfit(lefty, leftx, 2)\n",
    "    right_fit = np.polyfit(righty, rightx, 2)\n",
    "    #############填空12（2行代码）左侧、右侧车道线拟合#######################\n",
    "    \n",
    "    ################在图上画出拟合的线########################\n",
    "    ploty = np.linspace(0, undist.shape[0]-1, undist.shape[0])\n",
    "    \n",
    "    #########对y进行拟合，x = a * y ^ 2 + b * y + C\n",
    "    #############填空13（2行代码）左侧、右侧车道线方程坐标#######################\n",
    "    left_fitx = left_fit[0] * ploty ** 2 + left_fit[1] * ploty + left_fit[2]\n",
    "    right_fitx = right_fit[0] * ploty ** 2 + right_fit[1] * ploty + right_fit[2]\n",
    "    #############填空13（2行代码）左侧、右侧车道线方程坐标#######################\n",
    "    \n",
    "    ######生成一张黑图，做mask，将车道线区域标注出来##########\n",
    "    color_warp = np.zeros((720, 1280, 3), dtype='uint8')\n",
    "    pts_left = np.array([np.transpose(np.vstack([left_fitx, ploty]))])\n",
    "    pts_right = np.array([np.flipud(np.transpose(np.vstack([right_fitx, ploty])))])\n",
    "    pts = np.hstack((pts_left, pts_right))\n",
    "    # 在透射变换后的图上画出车道线\n",
    "    cv2.fillPoly(color_warp, np.int_([pts]), (0, 255, 0))\n",
    "    \n",
    "\n",
    "    # 将画出的车道线的图，逆变换到原来的图上，将color_warp逆变换为newwarp\n",
    "    #############填空14（1行代码）#######################\n",
    "    newwarp = cv2.warpPerspective(color_warp, m_inv, (undist.shape[1], undist.shape[0]))\n",
    "    #############填空14（1行代码）#######################\n",
    "    \n",
    "    \n",
    "    # 将原来的图和标注好车道线的图叠加，用cv2.addWeighted，可画成半透明，最终图为result\n",
    "    #############填空15（1行代码）#######################\n",
    "    result = cv2.addWeighted(cv2.cvtColor(undist, cv2.COLOR_BGR2RGB), 1, newwarp, 0.4, 0)\n",
    "    #############填空15（1行代码）#######################\n",
    "    \n",
    "    plt.figure(figsize = (30, 30))\n",
    "    plt.title('lane')\n",
    "    plt.subplot(1, 1, 1)\n",
    "    plt.imshow(result)\n",
    "    plt.axis('off')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/lorry/anaconda3/envs/cv-yolov5/lib/python3.7/site-packages/ipykernel_launcher.py:123: DeprecationWarning: `np.int` is a deprecated alias for the builtin `int`. To silence this warning, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.\n",
      "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n",
      "/home/lorry/anaconda3/envs/cv-yolov5/lib/python3.7/site-packages/ipykernel_launcher.py:136: DeprecationWarning: `np.int` is a deprecated alias for the builtin `int`. To silence this warning, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.\n",
      "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "expected non-empty vector for x",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m/tmp/ipykernel_289436/2936725782.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     23\u001b[0m \u001b[0mbinary_warped\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcv2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwarpPerspective\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimgOut\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mimgOut_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mflags\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcv2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mINTER_LINEAR\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     24\u001b[0m \u001b[0mundist\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcv2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimg_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 25\u001b[0;31m \u001b[0mline_fit_and_draw_line\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbinary_warped\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m/tmp/ipykernel_289436/2582885847.py\u001b[0m in \u001b[0;36mline_fit_and_draw_line\u001b[0;34m(binary_warped)\u001b[0m\n\u001b[1;32m    179\u001b[0m     \u001b[0;31m#将左侧，右侧车道线3次拟合，用函数np.polyfit##########\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    180\u001b[0m     \u001b[0;31m#############填空12（2行代码）左侧、右侧车道线拟合#######################\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 181\u001b[0;31m     \u001b[0mleft_fit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpolyfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlefty\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mleftx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    182\u001b[0m     \u001b[0mright_fit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpolyfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrighty\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrightx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    183\u001b[0m     \u001b[0;31m#############填空12（2行代码）左侧、右侧车道线拟合#######################\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<__array_function__ internals>\u001b[0m in \u001b[0;36mpolyfit\u001b[0;34m(*args, **kwargs)\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/cv-yolov5/lib/python3.7/site-packages/numpy/lib/polynomial.py\u001b[0m in \u001b[0;36mpolyfit\u001b[0;34m(x, y, deg, rcond, full, w, cov)\u001b[0m\n\u001b[1;32m    628\u001b[0m         \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"expected 1D vector for x\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    629\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 630\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"expected non-empty vector for x\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    631\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndim\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndim\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    632\u001b[0m         \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"expected 1D or 2D array for y\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mTypeError\u001b[0m: expected non-empty vector for x"
     ]
    },
    {
     "data": {
      "image/png": "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9saZk945t5m7T2+cuzfOnPTOuXvjzABA+ZWKoijKPQQAAAAAAAAAAEB36bV3mgIAAAAAAAAAANgQSlMAAAAAAAAAAEBFUZoCAAAAAAAAAAAqitIUAAAAAAAAAABQUXplaer73/9+xo0bl759+2bixIn505/+VLZZ/vjHP+b9739/GhsbUyqV8vOf/7zT80VR5JxzzkljY2P69euXqVOnZvbs2Z3WtLS05JRTTsnw4cMzYMCAHHHEEVm4cGGXzTx9+vTsu+++GTRoUEaMGJEjjzwyc+bM6fFzX3LJJdl9990zePDgDB48OJMnT85vfvObHj3zK02fPj2lUinTpk3r0XOfc845KZVKnR4NDQ09euYkeeaZZ/Kxj30sw4YNS//+/bPnnntm1qxZPXrubbbZ5lW/16VSKZ/97Gd77Mytra35yle+knHjxqVfv37Zdttt8/Wvfz3t7e0da3ri3GwaMnjjyGAZ/GZksAx+IzK4ssngjSODZfCbkcEy+I3I4MomgzeODJbBb0YGy+A3IoMBgC5X9DLXXHNNUVtbW/zgBz8oHn744eLUU08tBgwYUDz99NNlmefXv/51cfbZZxc//elPiyTFdddd1+n5b3zjG8WgQYOKn/70p8WDDz5YfOhDHypGjRpVNDc3d6w56aSTiq222qqYMWNGcc899xTvete7ij322KNobW3tkpkPPfTQ4oc//GHx0EMPFffdd1/xvve9r9h6662LFStW9Oi5r7/++uJ//ud/ijlz5hRz5swpvvzlLxe1tbXFQw891GNn/mt33XVXsc022xS77757ceqpp3Yc74lzf/WrXy123XXXYtGiRR2PJUuW9OiZX3zxxWLs2LHFJz7xieLOO+8s5s2bV9x0003F448/3qPnXrJkSaff5xkzZhRJij/84Q89duZzzz23GDZsWPGrX/2qmDdvXnHttdcWAwcOLL7zne90rOmJc7PxZPDGk8Ey+M3IYBn8RmRw5ZLBG08Gy+A3I4Nl8BuRwZVLBm88GSyD34wMlsFvRAYDAF2t15Wm9ttvv+Kkk07qdGynnXYqvvSlL5Vpor945Teq7e3tRUNDQ/GNb3yj49iaNWuK+vr64l//9V+LoiiKl156qaitrS2uueaajjXPPPNMUVVVVdxwww3dMveSJUuKJMUtt9zSq+YuiqIYMmRI8e///u89fubly5cX48ePL2bMmFEccMABHd+o9tS5v/rVrxZ77LHHaz7XU2f+4he/WLz97W9/3ed76tyvdOqppxbbbbdd0d7e3mNnft/73ld88pOf7HTsqKOOKj72sY8VRdF7fq9ZfzJ405PBMviVZLAMfiMyuHLJ4E1PBsvgV5LBMviNyODKJYM3PRksg19JBsvgNyKDAYCu1qs+nm/t2rWZNWtWDjnkkE7HDznkkNx2221lmur1zZs3L4sXL+40b11dXQ444ICOeWfNmpV169Z1WtPY2JgJEyZ02zk1NTUlSYYOHdpr5m5ra8s111yTlStXZvLkyT1+5s9+9rN53/vel4MOOqjT8Z4899y5c9PY2Jhx48blwx/+cJ588skePfP111+fffbZJ0cffXRGjBiRvfbaKz/4wQ86nu+pc/+1tWvX5qqrrsonP/nJlEqlHjvz29/+9vzud7/LY489liS5//77c+utt+a9731vkt7xe836k8FdQwbL4Ncig7tn7r8mg2VwTyaDu4YMlsGvRQZ3z9x/TQbL4J5MBncNGSyDX4sM7p65/5oMlsEAwMtqyj3A+njhhRfS1taWkSNHdjo+cuTILF68uExTvb4/z/Ra8z799NMda/r06ZMhQ4a8ak13nFNRFDnttNPy9re/PRMmTOjxcz/44IOZPHly1qxZk4EDB+a6667LLrvs0vEftj1x5muuuSb33HNPZs6c+arneurv9aRJk/KjH/0oO+ywQ5577rmce+65mTJlSmbPnt1jZ37yySdzySWX5LTTTsuXv/zl3HXXXfn85z+furq6HHfccT127r/285//PC+99FI+8YlPdMzTE2f+4he/mKampuy0006prq5OW1tb/umf/ikf+chHevTcbBwZvOnJYBn8WmSwDH4jMrgyyeBNTwbL4Ncig2XwG5HBlUkGb3oyWAa/Fhksg9+IDAYAulqvKk39WalU6vR1URSvOtaTbMi83XVOn/vc5/LAAw/k1ltvfdVzPXHuHXfcMffdd19eeuml/PSnP83xxx+fW265peP5njbzggULcuqpp+bGG29M3759X3ddT5v7sMMO6/j1brvtlsmTJ2e77bbLFVdckf333z9Jz5u5vb09++yzT84777wkyV577ZXZs2fnkksuyXHHHdexrqfN/dcuu+yyHHbYYWlsbOx0vKfN/JOf/CRXXXVVrr766uy666657777Mm3atDQ2Nub444/vsXOzacjgTUcGy+DXIoOz3ms2BRnc8/99jgzelGSwDH4tMjjrvWZTkME9/9/nyOBNSQbL4Ncig7PeazYFGdzz/30OAHSPXvXxfMOHD091dfWrmt9Llix5VYu8J2hoaEiSN5y3oaEha9euzbJly153TVc55ZRTcv311+cPf/hDRo8e3Svm7tOnT7bffvvss88+mT59evbYY49897vf7bEzz5o1K0uWLMnEiRNTU1OTmpqa3HLLLfne976Xmpqajn172tyvNGDAgOy2226ZO3duj/29HjVqVHbZZZdOx3beeefMnz+/Y6aeOPefPf3007npppvyqU99quNYT535H/7hH/KlL30pH/7wh7Pbbrvl4x//eL7whS9k+vTpPXpuNo4M3rRkcNfPLINl8Fslg7t+bjaODN60ZHDXzyyDZfBbJYO7fm42jgzetGRw188sg2XwWyWDu35uAKD36FWlqT59+mTixImZMWNGp+MzZszIlClTyjTV6xs3blwaGho6zbt27drccsstHfNOnDgxtbW1ndYsWrQoDz30UJedU1EU+dznPpef/exn+f3vf59x48b1irlfS1EUaWlp6bEzH3jggXnwwQdz3333dTz22WeffPSjH819992XbbfdtkfO/UotLS155JFHMmrUqB77e/22t70tc+bM6XTssccey9ixY5P0/D/XP/zhDzNixIi8733v6zjWU2detWpVqqo6x0d1dXXa29t79NxsHBm8achgGby+ZLAM/msyuDLJ4E1DBsvg9SWDZfBfk8GVSQZvGjJYBq8vGSyD/5oMBgC6XNHLXHPNNUVtbW1x2WWXFQ8//HAxbdq0YsCAAcVTTz1VlnmWL19e3HvvvcW9995bJCkuuOCC4t577y2efvrpoiiK4hvf+EZRX19f/OxnPysefPDB4iMf+UgxatSoorm5ueM9TjrppGL06NHFTTfdVNxzzz3Fu9/97mKPPfYoWltbu2Tmz3zmM0V9fX1x8803F4sWLep4rFq1qmNNT5z7rLPOKv74xz8W8+bNKx544IHiy1/+clFVVVXceOONPXbm13LAAQcUp556asfXPXHu008/vbj55puLJ598srjjjjuKww8/vBg0aFDH/8964sx33XVXUVNTU/zTP/1TMXfu3OLHP/5x0b9//+Kqq67qWNMT5y6Komhrayu23nrr4otf/OKrnuuJMx9//PHFVlttVfzqV78q5s2bV/zsZz8rhg8fXpx55pk9em42ngzeeDJYBr8ZGSyD34gMrlwyeOPJYBn8ZmSwDH4jMrhyyeCNJ4Nl8JuRwTL4jchgAKCr9brSVFEUxcUXX1yMHTu26NOnT7H33nsXt9xyS9lm+cMf/lAkedXj+OOPL4qiKNrb24uvfvWrRUNDQ1FXV1e8853vLB588MFO77F69eric5/7XDF06NCiX79+xeGHH17Mnz+/y2Z+rXmTFD/84Q871vTEuT/5yU92/HPfcsstiwMPPLDjm9SeOvNreeU3qj1x7g996EPFqFGjitra2qKxsbE46qijitmzZ/fomYuiKH75y18WEyZMKOrq6oqddtqpuPTSSzs931Pn/u1vf1skKebMmfOq53rizM3NzcWpp55abL311kXfvn2Lbbfdtjj77LOLlpaWHj03m4YM3jgyWAa/GRksg9+IDK5sMnjjyGAZ/GZksAx+IzK4ssngjSODZfCbkcEy+I3IYACgq5WKoig27b2rAAAAAAAAAAAAeq6qN18CAAAAAAAAAACw+VCaAgAAAAAAAAAAKorSFAAAAAAAAAAAUFGUpgAAAAAAAAAAgIqiNAUAAAAAAAAAAFQUpSkAAAAAAAAAAKCiKE0BAAAAAAAAAAAVRWkKAAAAAAAAAACoKEpTAAAAAAAAAABARVGaAgAAAAAAAAAAKorSFAAAAAAAAAAAUFGUpgAAAAAAAAAAgIry/wPDguGbHA8DDwAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 3000x3000 with 5 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "img_path = \"images/lane7.jpeg\"\n",
    "img = cv2.imread(img_path)\n",
    "img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n",
    "out1 = mag_threshold(img)\n",
    "out2 = abs_sobel_threshold(img)\n",
    "out3 = dir_threshold(img)\n",
    "out4 = hls_thresh(img)\n",
    "imgOut, abs_bin, mab_bin, dir_bin, hls_bin = combined_threshold(img)\n",
    "plt.figure(figsize = (30, 30))\n",
    "plt.title('calibration')\n",
    "plt.subplot(1, 5, 1)\n",
    "plt.imshow(abs_bin)\n",
    "plt.subplot(1, 5, 2)\n",
    "plt.imshow(mab_bin)\n",
    "plt.subplot(1, 5, 3)\n",
    "plt.imshow(dir_bin)\n",
    "plt.subplot(1, 5, 4)\n",
    "plt.imshow(hls_bin)\n",
    "plt.subplot(1, 5, 5)\n",
    "plt.imshow(imgOut)\n",
    "plt.axis('off')\n",
    "imgOut_size = (imgOut.shape[1], imgOut.shape[0])\n",
    "binary_warped = cv2.warpPerspective(imgOut, m, imgOut_size, flags=cv2.INTER_LINEAR)\n",
    "undist = cv2.imread(img_path)\n",
    "line_fit_and_draw_line(binary_warped)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
